package com.leyou.service;
import com.fasterxml.jackson.core.type.TypeReference;
import com.leyou.common.pojo.PageResult;
import com.leyou.common.utils.BeanHelper;
import com.leyou.common.utils.HighlightUtils;
import com.leyou.common.utils.JsonUtils;
import com.leyou.item.client.ItemClient;
import com.leyou.item.dto.SpuDTO;
import com.leyou.item.pojo.*;
import com.leyou.repository.SearchRepository;
import com.leyou.search.dto.GoodsDTO;
import com.leyou.search.dto.SearchRequest;
import com.leyou.search.dto.SearchResult;
import com.leyou.search.entity.Goods;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.AggregationPhase;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.query.FetchSourceFilter;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;


import java.util.*;
import java.util.stream.Collectors;
@Service
public class SearchService {
    @Autowired
    private SearchRepository searchRepository; //完成基本CRUD操作
    @Autowired
    private ElasticsearchTemplate esTemplate; //用于高级搜索
   @Autowired
    private ItemClient itemClient;

    /**
     * 批量导入数据到ES的方法
     */
    public void importData(){
         int page=1;
         int  rows=100;
         Long totalPage= 1L;
        do {
          PageResult<SpuDTO> pageResult = itemClient.spuPageQuery(page, rows, null, true);
            List<SpuDTO> spuDTOList = pageResult.getItems();
            List<Goods> goodsList = spuDTOList.stream().map(spuDTO -> buildGoods(spuDTO)).collect(Collectors.toList());
            searchRepository.saveAll(goodsList);
            page++;

            totalPage = pageResult.getTotalPage();

        } while (page<=totalPage);

    }
    /**
     * 把一个SpuDTO对象转换为一个Goods对象
     */
    public Goods buildGoods(SpuDTO spuDTO){
        Goods goods = new Goods();
        goods.setId(spuDTO.getId());
        goods.setSpuname(spuDTO.getName());
        goods.setSubTitle(spuDTO.getSubTitle());
        goods.setBrandId(spuDTO.getBrandId());
        goods.setCreateTime(new Date().getTime());
        goods.setCategoryId(spuDTO.getCid3());
        //处理skus，all，price
        List<Sku> skusList = itemClient.findSkusBySpuId(spuDTO.getId());
        //封装skus属性
        List<Map<String,Object>> skuMapList=new ArrayList<>();
        skusList.forEach(sku -> {
            Map<String, Object> skuMap = new HashMap<>();
            skuMap.put("id",sku.getId());
            skuMap.put("images",sku.getImages());
            skuMap.put("price",sku.getPrice());
            skuMapList.add(skuMap);
        });
        String skuJson = JsonUtils.toString(skuMapList);
        String all=spuDTO.getName()+" "+spuDTO.getSubTitle()+" "+skusList.stream().map(Sku::getTitle).collect(Collectors.joining(" "));
        List<Long> price = skusList.stream().map(Sku::getPrice).collect(Collectors.toList());
        goods.setSkus(skuJson);
        goods.setPrice(price);
        goods.setAll(all);
        //用于存储最后所有规格参数数据
        Map<String, Object> specs = new HashMap<>();
        List<SpecParam> specParams = itemClient.findSpecParam(null, spuDTO.getCid3(), true);
        SpuDetail spuDetail = itemClient.findSpuDetailBySpuId(spuDTO.getId());
        String genericSpec = spuDetail.getGenericSpec();
        String specialSpec = spuDetail.getSpecialSpec();
        specParams.forEach(specParam -> {
            Object value=null;
            String name = specParam.getName();
            if (specParam.getGeneric()){
                Map<Long, Object> genericSpecMap = JsonUtils.toMap(genericSpec, Long.class, Object.class);
                value= genericSpecMap.get(specParam.getId());
            }else{
                Map<Long, List<Object>> specialSpecMap = JsonUtils.nativeRead(specialSpec, new TypeReference<Map<Long, List<Object>>>() {});
               value = specialSpecMap.get(specParam.getId());
            }
            if (specParam.getNumeric()){
                value=chooseSegment(value,specParam);
            }
            specs.put(name,value);

        });
        goods.setSpecs(specs);
        return goods;

    }
    private String chooseSegment(Object value, SpecParam p) {
        if (value == null || StringUtils.isBlank(value.toString())) {
            return "其它";
        }
        double val = parseDouble(value.toString());
        String result = "其它";
        // 保存数值段
        for (String segment : p.getSegments().split(",")) {
            String[] segs = segment.split("-");
            // 获取数值范围
            double begin = parseDouble(segs[0]);
            double end = Double.MAX_VALUE;
            if (segs.length == 2) {
                end = parseDouble(segs[1]);
            }
            // 判断是否在范围内
            if (val >= begin && val < end) {
                if (segs.length == 1) {
                    result = segs[0] + p.getUnit() + "以上";
                } else if (begin == 0) {
                    result = segs[1] + p.getUnit() + "以下";
                } else {
                    result = segment + p.getUnit();
                }
                break;
            }
        }
        return result;
    }

    private double parseDouble(String str) {
        try {
            return Double.parseDouble(str);
        } catch (Exception e) {
            return 0;
        }
    }

    public SearchResult<GoodsDTO> goodsQuery(SearchRequest searchRequest) {
        //1.创建SearchResult<GoodsDTO>对象
        SearchResult<GoodsDTO> searchResult = new SearchResult<>();

        //2.封装SearchResult<GoodsDTO>对象
        //itemQueryPage(): 查询商品分页结果
        PageResult<GoodsDTO> pageResult=itemQueryPage(searchRequest);
        //filterConditionsQuery()

        Map<String,Object> filterConditions=filterConditionsQuery(searchRequest);
        //3.返回SearchResult<GoodsDTO>对象
        searchResult.setFilterConditions(filterConditions);

        searchResult.setTotal(pageResult.getTotal());

        searchResult.setTotalPage(pageResult.getTotalPage());

        searchResult.setItems(pageResult.getItems());

        return searchResult;

    }
    /**
     * 查询搜索过滤条件数据
     * @param searchRequest
     * @return
     */
    public Map<String, Object> filterConditionsQuery(SearchRequest searchRequest) {
        //1.创建Map对象
        //LinkedHashMap: 定义有序的Map集合
        Map<String, Object> filterConditions = new LinkedHashMap<>();

        //2.封装Map对象

        //1)构建基本的查询条件
        NativeSearchQueryBuilder queryBuilder = createNativeQueryBuilder(searchRequest);

        //2）添加结果过滤条件
        queryBuilder.withSourceFilter(new FetchSourceFilter(new String[]{""},null));

        //3）添加聚合条件
        String categoryAgg = "categoryAgg";
        String brandAgg = "brandAgg";

        //注意：所有Aggration条件都是AggrationBuilders构建来的
        queryBuilder.addAggregation(AggregationBuilders.terms(categoryAgg).field("categoryId"));
        queryBuilder.addAggregation(AggregationBuilders.terms(brandAgg).field("brandId"));

        //4）执行聚合查询
        //AggregatedPage是Page的子接口：Page只能封装分页结果，AggregatedPage既包含分页结果，又包含聚合结果
        AggregatedPage<Goods> aggregatedPage = esTemplate.queryForPage(queryBuilder.build(),Goods.class);

        //5)获取所有聚合结果
        Aggregations aggregations = aggregatedPage.getAggregations();

        //7）先取出分类聚合结果
        Terms categoryTerms = aggregations.get(categoryAgg);
        List<Long> categoryIds =  categoryTerms.getBuckets()
                .stream()
                .map(Terms.Bucket::getKeyAsNumber)//把key取出转换Number类型
                .map(Number::longValue) // 把上一步的Number类型转换为Long类型
                .collect(Collectors.toList());

        List<Category> categoryList =  itemClient.findCategoriesByCids(categoryIds);

        //8）取出品牌聚合结果
        Terms brandTerms = aggregations.get(brandAgg);
        List<Long> brandIds =  brandTerms.getBuckets()
                .stream()
                .map(Terms.Bucket::getKeyAsNumber)//把key取出转换Number类型
                .map(Number::longValue) // 把上一步的Number类型转换为Long类型
                .collect(Collectors.toList());

        List<Brand> brandList =  itemClient.findBrandsByIds(brandIds);

        filterConditions.put("分类",categoryList);
        filterConditions.put("品牌",brandList);

        if (categoryIds !=null){
            categoryIds.forEach(categoryId->{
                //1）根据分类ID查询参与搜索过滤条件的规格参数
                List<SpecParam> specParams = itemClient.findSpecParam(null, categoryId, true);
                //2）遍历规格参数，逐个添加到聚合条件中
                 specParams.forEach(specParam -> {
                     queryBuilder.addAggregation(AggregationBuilders.terms(specParam.getName()).field("specs."+specParam.getName()+".keyword"));
                 });
                //3）执行聚合查询
                AggregatedPage<Goods> specParamAggPage = esTemplate.queryForPage(queryBuilder.build(), Goods.class);
                Aggregations specParamAggs = specParamAggPage.getAggregations();
                //4）遍历规格参数， 获取所有规格参数的聚合结果，把每个规格参数的聚合结果存入Map集合
                specParams.forEach(specParam -> {
                Terms aggregation = specParamAggs.get(specParam.getName());
                    List<Object> specParamAggKeyList = aggregation.getBuckets().stream().map(Terms.Bucket::getKey).collect(Collectors.toList());
                    filterConditions.put(specParam.getName(),specParamAggKeyList);
                });

            });

        }
        //3.返回Map对象
        return filterConditions;
    }
    /**
     * 查询商品分页结果
     * @param searchRequest
     * @return
     */
    private PageResult<GoodsDTO> itemQueryPage(SearchRequest searchRequest) {
     /*   //1.创建本地查询构造器对象
        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
        //2.设置条件(*)
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        boolQueryBuilder.must(QueryBuilders.multiMatchQuery(searchRequest.getKey(),"all","spuname"));
        queryBuilder.withQuery(boolQueryBuilder);*/

        NativeSearchQueryBuilder queryBuilder = createNativeQueryBuilder(searchRequest);
        queryBuilder.withSourceFilter(new FetchSourceFilter(new String[]{"id","spuname","skus","subTitle"},null));
        //2.3 添加分页条件
        queryBuilder.withPageable(PageRequest.of(searchRequest.getPage()-1,searchRequest.getSize()));
        HighlightUtils.highlightField(queryBuilder,"spuname");

        Page<Goods> pageBean = esTemplate.queryForPage(queryBuilder.build(), Goods.class,HighlightUtils.highlightBody(Goods.class,"spuname"));

        List<Goods> goodsList = pageBean.getContent();

        List<GoodsDTO> goodsDTOList = BeanHelper.copyWithCollection(goodsList, GoodsDTO.class);

        PageResult<GoodsDTO> pageResult = new PageResult<>(pageBean.getTotalElements(),Long.valueOf(pageBean.getTotalPages()), goodsDTOList);
        return pageResult;
    }

    /**
     * 构建基本的查询条件
     * @param searchRequest
     * @return
     */
    public NativeSearchQueryBuilder createNativeQueryBuilder(SearchRequest searchRequest) {
        //1.创建本地查询构造器对象
        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();

        //2.设置条件(*)
        //2.1 添加Query条件
        //注意：Query条件都是QueryBuilders构造来的
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        //往bool条件中添加must条件
        //boolQueryBuilder.must(QueryBuilders.matchQuery("all",searchRequest.getKey()));
        boolQueryBuilder.must(QueryBuilders.multiMatchQuery(searchRequest.getKey(),"spuname","all"));
        Map<String, Object> filterParams = searchRequest.getFilterParams();
        if (filterParams != null) {
           filterParams.entrySet().forEach(entity->{
               Object value = entity.getValue();
               String key = entity.getKey();
               if (key.equals("分类")){
                   key="categoryId";
               }else if (key.equals("品牌")){
                   key="brandId";
               }else{
                   key="specs."+key+".keyword";
               }
               boolQueryBuilder.filter(QueryBuilders.termQuery(key,value));
           });
        }
        queryBuilder.withQuery(boolQueryBuilder);
        return queryBuilder;
    }


    public List<GoodsDTO>  goodChange(SearchRequest searchRequest) {
        PageResult<GoodsDTO> pageResult = itemQueryPage(searchRequest);
        List<GoodsDTO> goodsDTOList = pageResult.getItems();
        return goodsDTOList;
    }
    //创建索引库
    public void createIndex(Long spuId) {
        SpuDTO spuDTO = itemClient.findSpuDTOBySpuId(spuId);
        Goods goods = buildGoods(spuDTO);
        searchRepository.save(goods);

    }
   //删除索引库
    public void deleteIndex(Long spuId) {
       searchRepository.deleteById(spuId);
    }
}
